package com.yunxin.mybatisplus.utils.datahandler;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.yunxin.mybatisplus.bean.DecimalSerializeProperties;
import org.apache.logging.log4j.util.Strings;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Objects;

/**
 * 自定义序列化器，用于处理 BigDecimal, Double, Float, Date、Long 等类型的序列化，
 * 支持通过 @DataProcess 注解和全局配置控制精度与格式。
 */
public class DecimalSerializer extends JsonSerializer<Object> implements ContextualSerializer {

    /**
     * 配置项：精度
     */
    private final Integer scale;
    /**
     * 配置项：舍入模式
     */
    private final RoundingMode roundingMode;
    /**
     * 配置项：时间格式
     */
    private final String datePattern;
    /**
     * 配置项：时区
     */
    private final String timeZone;

    /**
     * 缓存全局配置，避免每次 serialize 都 getBean
     */
    private final DecimalSerializeProperties globalConfig;

    /**
     * 主构造函数，接收所有可能的配置
     *
     * @param scale        精度
     * @param roundingMode 舍入模式
     * @param datePattern  时间格式
     * @param timeZone     时区
     */
    public DecimalSerializer(Integer scale, String roundingMode, String datePattern, String timeZone) {
        this.scale = scale;
        this.roundingMode = getRoundingModeEnum(roundingMode);
        this.datePattern = datePattern;
        this.timeZone = timeZone;
        this.globalConfig = SpringContextUtil.getBean(DecimalSerializeProperties.class);
    }

    /**
     * 序列化方法
     *
     * @param value
     * @param gen
     * @param serializers
     * @throws IOException
     */
    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (value == null) {
            gen.writeNull();
            return;
        }
        try {
            if (value instanceof BigDecimal) {
                writeScaledNumber((BigDecimal) value, gen);
            } else if (value instanceof Double) {
                writeScaledNumber(BigDecimal.valueOf((Double) value), gen);
            } else if (value instanceof Float) {
                writeScaledNumber(BigDecimal.valueOf((Float) value), gen);
            } else if (value instanceof Date) {
                writeFormattedDate((Date) value, gen);
            } else if (value instanceof Long) {
                gen.writeString(value.toString());
            } else {
                // 其他类型走默认序列化
                serializers.defaultSerializeValue(value, gen);
            }
        } catch (Exception e) {
            // 序列化过程中出现任何异常，回退到写入原始值或 null
            serializers.defaultSerializeValue(value, gen);
        }
    }

    /**
     * 创建可上下文化的序列化器
     * @param prov
     * @param property
     * @return
     * @throws JsonMappingException
     */
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        if (property == null) {
            return this;
        }

        // 处理 @DataProcess：字段 > 类 > 全局
        DataProcess keepDecimal = property.getAnnotation(DataProcess.class);
        if (keepDecimal == null) {
            keepDecimal = property.getContextAnnotation(DataProcess.class);
        }

        Integer finalScale = null;
        String finalRoundingMode = null;
        String finalPattern = null;
        String finalTimeZone = null;

        if (globalConfig != null) {
            finalScale = globalConfig.getDefaultScale();
            finalRoundingMode = globalConfig.getRoundingMode();
            finalPattern = globalConfig.getDateFormat();
            finalTimeZone = globalConfig.getTimeZone();
        }

        if (keepDecimal != null && keepDecimal.value() != -1) {
            finalScale = keepDecimal.value();
        }
        if (keepDecimal != null && Strings.isNotBlank(keepDecimal.pattern())) {
            finalPattern = keepDecimal.pattern();
        }
        if (keepDecimal != null && Strings.isNotBlank(keepDecimal.timezone())) {
            finalTimeZone = keepDecimal.timezone();
        }
        if (keepDecimal != null && Strings.isNotBlank(keepDecimal.roundingMode())) {
            finalRoundingMode = keepDecimal.roundingMode();
        }

        // 优化：只有当配置发生变化时才创建新实例
        if (!Objects.equals(finalScale, this.scale) ||
                !Objects.equals(finalRoundingMode, this.roundingMode != null ? this.roundingMode.name() : null) ||
                !Objects.equals(finalPattern, this.datePattern) ||
                !Objects.equals(finalTimeZone, this.timeZone)) {
            return new DecimalSerializer(finalScale, finalRoundingMode, finalPattern, finalTimeZone);
        }

        return this;
    }

    /**
     * 处理 BigDecimal 类型的序列化
     * @param value
     * @param gen
     * @throws IOException
     */
    private void writeScaledNumber(BigDecimal value, JsonGenerator gen) throws IOException {
        Integer tempScale = scale != null ? scale :
                (globalConfig != null ? globalConfig.getDefaultScale() : DecimalSerializeProperties.DEFAULT_SCALE);
        RoundingMode mode = roundingMode != null ? roundingMode :
                getRoundingModeEnum(globalConfig != null ? globalConfig.getRoundingMode() : DecimalSerializeProperties.DEFAULT_ROUNDING_MODE);

        value = value.setScale(tempScale, mode);
        gen.writeNumber(value);
    }

    /**
     * 处理 Date 类型的序列化
     * @param date
     * @param gen
     * @throws IOException
     */
    private void writeFormattedDate(Date date, JsonGenerator gen) throws IOException {
        String pattern = datePattern != null ? datePattern :
                (globalConfig != null ? globalConfig.getDateFormat() : DecimalSerializeProperties.DEFAULT_DATE_FORMAT);
        String zone = timeZone != null ? timeZone :
                (globalConfig != null ? globalConfig.getTimeZone() : DecimalSerializeProperties.DEFAULT_TIME_ZONE);

        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
        sdf.setTimeZone(java.util.TimeZone.getTimeZone(zone));
        gen.writeString(sdf.format(date));
    }

    /**
     * 获取 RoundingMode
     *
     * @param roundingMode
     * @return
     */
    public RoundingMode getRoundingModeEnum(String roundingMode) {
        if (Strings.isBlank(roundingMode)) {
            roundingMode=DecimalSerializeProperties.DEFAULT_ROUNDING_MODE;
        }
        try {
            return RoundingMode.valueOf(roundingMode.trim().toUpperCase());
        } catch (IllegalArgumentException e) {
            // 如果配置错误，回退到默认
            return RoundingMode.HALF_UP;
        }
    }
}